package com.ruoyi.collect.engine.client;

import com.ruoyi.collect.engine.parser.IResourceParser;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.net.ssl.*;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * MacCMS API 客户端 - 使用OkHttp（HTTP/2支持，更好的Cloudflare兼容性）
 *
 * @author ruoyi
 * @date 2025-11-08
 */
@Component
public class MacCmsApiClient {

    private static final Logger log = LoggerFactory.getLogger(MacCmsApiClient.class);

    // 超时配置（单位：秒）- 参考MacCMS设置
    private static final int CONNECT_TIMEOUT = 15;  // MacCMS使用15秒
    private static final int READ_TIMEOUT = 30;      // MacCMS使用30秒
    private static final int WRITE_TIMEOUT = 30;

    // 使用与MacCMS相同的User-Agent
    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36";
    private static final int MAX_RETRY_TIMES = 3;

    // 请求间隔配置（单位：毫秒）
    private static final int MIN_REQUEST_INTERVAL = 1000;
    private static final int MAX_REQUEST_INTERVAL = 3000;

    private OkHttpClient okHttpClient;
    private long lastRequestTime = 0;
    private final Object requestLock = new Object();

    @Autowired(required = false)
    private List<IResourceParser> parsers;

    /**
     * 创建OkHttp客户端（支持HTTP/2和自签名证书）
     */
    private OkHttpClient createOkHttpClient() {
        try {
            // 创建信任所有证书的TrustManager
            TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
            };

            // 安装信任所有证书的SSLContext
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            return new OkHttpClient.Builder()
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true) // 不验证主机名
                    .retryOnConnectionFailure(true) // 连接失败时自动重试
                    .followRedirects(true) // 自动跟随重定向
                    .followSslRedirects(true)
                    .build();

        } catch (Exception e) {
            log.error("Failed to create OkHttp client: {}", e.getMessage(), e);
            // 降级到普通客户端
            return new OkHttpClient.Builder()
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                    .build();
        }
    }

    /**
     * 获取OkHttp客户端（懒加载）
     */
    private OkHttpClient getOkHttpClient() {
        if (okHttpClient == null) {
            synchronized (this) {
                if (okHttpClient == null) {
                    okHttpClient = createOkHttpClient();
                }
            }
        }
        return okHttpClient;
    }

    /**
     * 请求间隔控制 - 避免请求过于频繁
     */
    private void waitForRequestInterval() {
        synchronized (requestLock) {
            long currentTime = System.currentTimeMillis();
            long timeSinceLastRequest = currentTime - lastRequestTime;

            if (lastRequestTime > 0 && timeSinceLastRequest < MIN_REQUEST_INTERVAL) {
                // 计算需要等待的时间（随机化以更像人类行为）
                long waitTime = MIN_REQUEST_INTERVAL +
                               (long)(Math.random() * (MAX_REQUEST_INTERVAL - MIN_REQUEST_INTERVAL));
                waitTime = Math.max(0, waitTime - timeSinceLastRequest);

                if (waitTime > 0) {
                    log.debug("Waiting {} ms before next request (anti-crawler)", waitTime);
                    try {
                        Thread.sleep(waitTime);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        log.warn("Request interval wait interrupted");
                    }
                }
            }

            lastRequestTime = System.currentTimeMillis();
        }
    }

    /**
     * 获取列表数据
     *
     * @param siteUrl 站点URL
     * @param request 请求参数
     * @return MacCMS响应对象
     */
    public MacCmsResponse getList(String siteUrl, MacCmsRequest request) {
        return getList(siteUrl, request, null);
    }

    /**
     * 获取列表数据（支持指定数据格式）
     *
     * @param siteUrl 站点URL
     * @param request 请求参数
     * @param dataFormat 数据格式 (0=自动, 1=XML, 2=JSON, null=自动)
     * @return MacCMS响应对象
     */
    public MacCmsResponse getList(String siteUrl, MacCmsRequest request, String dataFormat) {
        // 请求间隔控制
        waitForRequestInterval();

        String url = buildUrl(siteUrl, request);
        log.info("Collect URL: {}", url);

        String content = null;
        int retryCount = 0;
        Exception lastException = null;

        // Retry mechanism
        while (retryCount < MAX_RETRY_TIMES) {
            try {
                content = httpGet(url);
                if (content != null && !content.isEmpty()) {
                    break;
                }
            } catch (Exception e) {
                lastException = e;
                retryCount++;
                log.warn("Request failed, retrying ({}/{}): {}", retryCount, MAX_RETRY_TIMES, e.getMessage());

                if (retryCount < MAX_RETRY_TIMES) {
                    try {
                        // 指数退避策略：2秒、5秒、10秒
                        long backoffTime = (long)(Math.pow(2, retryCount) * 1000 + Math.random() * 1000);
                        log.info("Waiting {} ms before retry {} (exponential backoff)", backoffTime, retryCount + 1);
                        Thread.sleep(backoffTime);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }

        if (content == null || content.isEmpty()) {
            String errorMsg = lastException != null ? lastException.getMessage() : "Unknown error";
            log.error("Collection failed: {}", errorMsg);
            throw new RuntimeException("Collection failed: " + errorMsg);
        }

        return parseResponse(content, dataFormat);
    }

    /**
     * 获取详情数据
     *
     * @param siteUrl 站点URL
     * @param ids 视频ID列表
     * @return MacCMS响应对象
     */
    public MacCmsResponse getDetail(String siteUrl, String ids) {
        return getDetail(siteUrl, ids, null);
    }

    /**
     * 获取详情数据（支持指定数据格式）
     *
     * @param siteUrl 站点URL
     * @param ids 视频ID列表
     * @param dataFormat 数据格式 (0=自动, 1=XML, 2=JSON, null=自动)
     * @return MacCMS响应对象
     */
    public MacCmsResponse getDetail(String siteUrl, String ids, String dataFormat) {
        MacCmsRequest request = new MacCmsRequest();
        request.setAc("detail");
        request.setIds(ids);
        return getList(siteUrl, request, dataFormat);
    }

    /**
     * 获取分类列表
     */
    public List<MacCmsResponse.TypeInfo> getTypeList(String siteUrl) {
        MacCmsRequest request = new MacCmsRequest();
        request.setAc("list");
        request.setPg(1);
        request.setPagesize(1);

        MacCmsResponse response = getList(siteUrl, request);
        return response.getTypeList();
    }

    /**
     * 构建完整URL
     */
    private String buildUrl(String baseUrl, MacCmsRequest request) {
        if (!baseUrl.endsWith("/") && !baseUrl.contains("?")) {
            baseUrl += "/";
        }

        String queryString = request.toQueryString();
        if (baseUrl.contains("?")) {
            return baseUrl + queryString.substring(1);
        } else {
            return baseUrl + queryString;
        }
    }

    /**
     * 执行HTTP GET请求（多种方案自动降级）
     * 方案1: Selenium Firefox（完全模拟浏览器，绕过Cloudflare TLS指纹）
     * 方案2: 系统curl（使用libcurl）
     * 方案3: OkHttp（Java原生实现）
     */
    private String httpGet(String url) throws IOException {
        log.info("Executing HTTP GET: {}", url);

        // 方案1: 尝试使用Selenium Firefox（与用户浏览器相同）
        String seleniumResult = httpGetWithSelenium(url);
        if (seleniumResult != null) {
            log.info("Successfully fetched using Selenium Firefox");
            return seleniumResult;
        }

        // 方案2: 尝试使用系统curl（与MacCMS PHP使用相同的libcurl）
        String curlResult = httpGetWithSystemCurl(url);
        if (curlResult != null) {
            log.info("Successfully fetched using system curl");
            return curlResult;
        }

        // 方案3: 降级到OkHttp
        log.warn("Selenium and curl not available, fallback to OkHttp");
        return httpGetWithOkHttp(url);
    }

    /**
     * 使用Selenium Firefox执行HTTP GET请求
     * 完全模拟真实Firefox浏览器，绕过Cloudflare TLS指纹识别
     */
    private String httpGetWithSelenium(String url) {
        WebDriver driver = null;
        try {
            // 检查 Firefox 是否安装
            String firefoxPath = findFirefoxBinary();
            if (firefoxPath == null) {
                log.debug("Firefox browser not installed, skipping Selenium");
                return null;
            }

            // 配置Firefox选项（无头模式）
            FirefoxOptions options = new FirefoxOptions();
            options.setBinary(firefoxPath);  // 显式设置 Firefox 路径
            options.setHeadless(true);  // 无界面模式（3.141.59 版本的正确方法）
            options.addArguments("--disable-gpu");
            options.addArguments("--no-sandbox");

            // 设置 Firefox Profile 以避免被检测
            FirefoxProfile profile = new FirefoxProfile();
            profile.setPreference("general.useragent.override", USER_AGENT);
            profile.setPreference("dom.webdriver.enabled", false);
            profile.setPreference("useAutomationExtension", false);
            options.setProfile(profile);

            // 创建Firefox驱动
            driver = new FirefoxDriver(options);

            // 设置超时
            driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
            driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

            log.info("Selenium Firefox: Loading URL: {}", url);

            // 访问URL
            driver.get(url);

            // 等待页面加载完成
            Thread.sleep(3000);

            // 获取页面源码
            String pageSource = driver.getPageSource();

            if (pageSource != null && pageSource.length() > 100) {
                // 检查是否是 JSON 响应（而不是 HTML 错误页面）
                if (pageSource.contains("<!DOCTYPE") || pageSource.contains("<html")) {
                    // 如果是 HTML，尝试提取 body 内容
                    try {
                        String bodyContent = driver.findElement(org.openqa.selenium.By.tagName("body")).getText();
                        if (bodyContent != null && bodyContent.trim().startsWith("{")) {
                            log.info("Selenium Firefox succeeded (extracted JSON from HTML), response length: {}", bodyContent.length());
                            return bodyContent;
                        }
                    } catch (Exception ex) {
                        log.debug("Failed to extract body content: {}", ex.getMessage());
                    }
                }
                log.info("Selenium Firefox succeeded, response length: {}", pageSource.length());
                return pageSource;
            } else {
                log.warn("Selenium Firefox returned empty or invalid content");
                return null;
            }

        } catch (Exception e) {
            log.debug("Selenium Firefox not available or failed: {}", e.getMessage());
            return null;
        } finally {
            if (driver != null) {
                try {
                    driver.quit();
                } catch (Exception e) {
                    log.debug("Failed to quit driver: {}", e.getMessage());
                }
            }
        }
    }

    /**
     * 查找 Firefox 安装路径
     */
    private String findFirefoxBinary() {
        // Windows 常见 Firefox 安装路径
        String[] windowsPaths = {
            "C:\\Program Files\\Mozilla Firefox\\firefox.exe",
            "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe",
            System.getenv("LOCALAPPDATA") + "\\Mozilla Firefox\\firefox.exe",
            System.getenv("PROGRAMFILES") + "\\Mozilla Firefox\\firefox.exe",
            System.getenv("PROGRAMFILES(X86)") + "\\Mozilla Firefox\\firefox.exe"
        };

        for (String path : windowsPaths) {
            if (path != null) {
                java.io.File file = new java.io.File(path);
                if (file.exists()) {
                    log.debug("Found Firefox at: {}", path);
                    return path;
                }
            }
        }

        log.debug("Firefox not found in common locations");
        return null;
    }

    /**
     * 使用系统curl命令执行HTTP GET请求
     * 这样可以使用与MacCMS PHP相同的libcurl库，绕过Java TLS指纹识别
     */
    private String httpGetWithSystemCurl(String url) {
        try {
            // 构建curl命令 - 完全模拟MacCMS的curl设置
            ProcessBuilder pb = new ProcessBuilder(
                "curl",
                "-s",  // 静默模式
                "-L",  // 跟随重定向
                "--connect-timeout", "15",  // 连接超时15秒（与MacCMS一致）
                "--max-time", "30",         // 总超时30秒（与MacCMS一致）
                "-k",  // 不验证SSL证书（与MacCMS CURLOPT_SSL_VERIFYPEER=0一致）
                "-H", "User-Agent: " + USER_AGENT,
                "-H", "Referer: " + url,
                "-H", "Accept: */*",
                "-H", "Accept-Language: zh-CN,zh;q=0.9",
                url
            );

            pb.redirectErrorStream(true);  // 合并stderr到stdout

            Process process = pb.start();

            // 读取输出
            StringBuilder output = new StringBuilder();
            try (java.io.BufferedReader reader = new java.io.BufferedReader(
                    new java.io.InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                }
            }

            int exitCode = process.waitFor();

            if (exitCode == 0 && output.length() > 0) {
                log.info("System curl succeeded, response length: {}", output.length());
                return output.toString();
            } else {
                log.warn("System curl failed with exit code: {}", exitCode);
                return null;
            }

        } catch (Exception e) {
            log.debug("System curl not available or failed: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 使用OkHttp执行HTTP GET请求（降级方案）
     */
    private String httpGetWithOkHttp(String url) throws IOException {
        // 完全模拟MacCMS PHP curl的请求方式
        // MacCMS关键：设置Referer为URL自身，简单的headers
        // 注意：不要手动设置 Accept-Encoding，让 OkHttp 自动处理 gzip 解压
        Request request = new Request.Builder()
                .url(url)
                .addHeader("User-Agent", USER_AGENT)
                .addHeader("Referer", url)  // ← MacCMS的关键设置！
                .addHeader("Accept", "*/*")
                .addHeader("Accept-Language", "zh-CN,zh;q=0.9")
                // 移除 Accept-Encoding，让 OkHttp 自动处理 gzip
                .build();

        try (Response response = getOkHttpClient().newCall(request).execute()) {
            int statusCode = response.code();
            log.info("Response status: {}", statusCode);

            if (statusCode != 200) {
                throw new IOException("HTTP request failed, status code: " + statusCode);
            }

            ResponseBody body = response.body();
            if (body == null) {
                throw new IOException("Response body is empty");
            }

            // 使用 string() 方法而不是 bytes()，让 OkHttp 自动处理解压和字符编码
            String content = body.string();
            log.info("Response received successfully, content length: {} chars", content.length());

            return content;

        } catch (IOException e) {
            log.error("HTTP GET failed for URL: {}, Error: {}", url, e.getMessage());
            throw e;
        }
    }

    /**
     * Parse response content (自动检测格式)
     *
     * @param content 响应内容
     * @return MacCMS响应对象
     */
    private MacCmsResponse parseResponse(String content) {
        return parseResponse(content, null);
    }

    /**
     * Parse response content (支持强制指定格式)
     *
     * @param content 响应内容
     * @param dataFormat 数据格式 (0=自动, 1=XML, 2=JSON, null=自动)
     * @return MacCMS响应对象
     */
    private MacCmsResponse parseResponse(String content, String dataFormat) {
        if (parsers == null || parsers.isEmpty()) {
            log.warn("No available parsers");
            throw new RuntimeException("No available parsers");
        }

        // Log registered parsers
        log.info("Available parsers: {}", parsers.stream()
            .map(p -> p.getClass().getSimpleName())
            .collect(java.util.stream.Collectors.joining(", ")));

        // 强制指定格式解析
        if (dataFormat != null && !dataFormat.equals("0")) {
            String targetParserType = null;
            if ("1".equals(dataFormat)) {
                targetParserType = "XML";
                log.info("强制使用XML解析器 (dataFormat=1)");
            } else if ("2".equals(dataFormat)) {
                targetParserType = "JSON";
                log.info("强制使用JSON解析器 (dataFormat=2)");
            }

            if (targetParserType != null) {
                for (IResourceParser parser : parsers) {
                    if (targetParserType.equals(parser.getType())) {
                        log.info("Using forced parser: {}", parser.getType());
                        try {
                            return parser.parse(content);
                        } catch (Exception e) {
                            log.error("Forced parser {} failed: {}", parser.getType(), e.getMessage(), e);
                            throw new RuntimeException("强制使用" + targetParserType + "解析失败: " + e.getMessage(), e);
                        }
                    }
                }
                log.error("未找到指定的{}解析器", targetParserType);
                throw new RuntimeException("未找到指定的" + targetParserType + "解析器");
            }
        }

        // Auto detect format and parse (dataFormat=0 或 null)
        log.info("自动检测数据格式 (dataFormat={})...", dataFormat == null ? "null" : dataFormat);
        for (IResourceParser parser : parsers) {
            log.debug("Checking parser: {}, support result: {}", parser.getType(), parser.support(content));
            if (parser.support(content)) {
                log.info("Using parser: {}", parser.getType());
                try {
                    return parser.parse(content);
                } catch (Exception e) {
                    log.error("Parse failed: {}", e.getMessage(), e);
                }
            }
        }

        // Try force use each parser
        for (IResourceParser parser : parsers) {
            try {
                log.info("Trying parser: {}", parser.getType());
                MacCmsResponse response = parser.parse(content);
                if (response != null) {
                    return response;
                }
            } catch (Exception e) {
                log.debug("Parser {} cannot parse: {}", parser.getType(), e.getMessage());
            }
        }

        log.error("All parsers failed to parse response content");
        log.error("Response content length: {}", content.length());
        log.error("Response content first 1000 chars: {}", content.length() > 1000 ? content.substring(0, 1000) : content);

        String trimmed = content.trim();
        log.error("Response content (trimmed) length: {}", trimmed.length());
        if (trimmed.length() > 0) {
            log.error("Response first 10 chars (hex): {}", bytesToHex(trimmed.substring(0, Math.min(10, trimmed.length())).getBytes()));
            log.error("Response first 100 chars: '{}'", trimmed.substring(0, Math.min(100, trimmed.length())));
        }

        log.error("Response content type detection:");
        log.error("  Starts with '{{': {}", trimmed.startsWith("{"));
        log.error("  Starts with '<?xml': {}", trimmed.startsWith("<?xml"));
        log.error("  Starts with '<': {}", trimmed.startsWith("<"));
        throw new RuntimeException("Unsupported data format or parse failed");
    }

    /**
     * Test connection
     */
    public boolean testConnection(String siteUrl) {
        try {
            MacCmsRequest request = new MacCmsRequest();
            request.setAc("list");
            request.setPg(1);
            request.setPagesize(1);

            MacCmsResponse response = getList(siteUrl, request);
            return response != null && (response.getCode() == null || response.getCode() == 1);
        } catch (Exception e) {
            log.error("Test connection failed: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 字节数组转十六进制字符串（用于调试）
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }
}
